package com.dag.account.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dag.account.api.dto.account.CountAccountDTO;
import com.dag.account.api.dto.account.OpenAccountDTO;
import com.dag.account.constants.AccountStatusEnum;
import com.dag.account.constants.ObjTypeEnum;
import com.dag.account.constants.OperateEventEnum;
import com.dag.account.dao.entity.Account;
import com.dag.account.dao.entity.OperateRecord;
import com.dag.account.dao.mapper.AccountMapper;
import com.dag.account.dao.vo.OperateBalanceVO;
import com.dag.account.dao.vo.OperateFreezeVO;
import com.dag.account.service.IAccountService;
import com.dag.account.service.IOperateRecordService;
import com.dag.common.exception.BusinessRuntimeException;
import com.dag.common.utils.ModelMapperUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * 账户操作类
 * @author 孙建
 */
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements IAccountService {

    @Autowired
    private IOperateRecordService operateRecordService;

    /** 金额单位 */
    private static final String UNIT = "元";

    /**
     * 开通账户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Account openAccount(OpenAccountDTO openAccountDTO) {
        // 校验
        Account existsAccount = queryByMemberIdAndAccountType(openAccountDTO.getMemberId(), openAccountDTO.getAccountType());
        if(existsAccount != null){
            throw new BusinessRuntimeException("开通失败，此账户已存在");
        }

        // 保存账户
        Account account = ModelMapperUtils.map(openAccountDTO, Account.class);
        account.setStatus(AccountStatusEnum.VALID.getIndex());
        account.setTotalAmount(BigDecimal.ZERO);
        account.setBalanceAmount(BigDecimal.ZERO);
        account.setFreezeAmount(BigDecimal.ZERO);
        account.setVersion(0L);
        account.setCreateTime(new Date());
        account.setCreaterId("1");
        account.setUpdaterId("1");
        save(account);

        // 保存操作记录
        String description = openAccountDTO.getOperatorName() + "开通了账户(" + openAccountDTO.getAccountTypeName() + ")";
        this.saveOperateRecord(account, openAccountDTO.getOperatorName(), OperateEventEnum.OPEN.getText(), description);
        return account;
    }


    @Override
    public List<Account> queryByMemberId(String memberId) {
        Account account = new Account();
        account.setMemberId(memberId);
        return list(new QueryWrapper<>(account));
    }


    /**
     * 账户合计汇总
     */
    @Override
    public CountAccountDTO summary(String memberId) {
        return baseMapper.summary(memberId);
    }


    /**
     * 根据会员id和账户类型查询账户
     */
    @Override
    public Account queryByMemberIdAndAccountType(String memberId, Integer accountType) {
        Account account = new Account();
        account.setMemberId(memberId);
        account.setAccountType(accountType);
        return getOne(new QueryWrapper<>(account));
    }


    /**
     * 增加余额
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(Account account, BigDecimal amount, String operatorName) {
        amount = amount.abs();
        OperateBalanceVO operateBalanceVO = new OperateBalanceVO(account.getId(), amount);
        baseMapper.operateBalance(operateBalanceVO);

        // 保存操作记录
        String description = operatorName + "操作" + account.getAccountTypeName() + "，入账" + amount + UNIT;
        this.saveOperateRecord(account, operatorName, OperateEventEnum.ADD_MONEY.getText(), description);
    }


    /**
     * 扣除余额
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deduction(Account account, BigDecimal amount, String operatorName) {
        amount = amount.abs().multiply(BigDecimal.valueOf(-1));
        OperateBalanceVO operateBalanceVO = new OperateBalanceVO(account.getId(), amount);
        baseMapper.operateBalance(operateBalanceVO);

        // 保存操作记录
        String description = operatorName + "操作" + account.getAccountTypeName() + "，扣款" + amount + UNIT;
        this.saveOperateRecord(account, operatorName, OperateEventEnum.DEDUCTION_MONEY.getText(), description);
    }


    /**
     * 冻结
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void freeze(Account account, BigDecimal amount, String operatorName) {
        amount = amount.abs();
        OperateFreezeVO operateFreezeVO = new OperateFreezeVO(account.getId(), amount);
        baseMapper.operateFreeze(operateFreezeVO);

        // 保存操作记录
        String description = operatorName + "操作" + account.getAccountTypeName() + "，冻结" + amount + UNIT;
        this.saveOperateRecord(account, operatorName, OperateEventEnum.FREEZE_MONEY.getText(), description);
    }


    /**
     * 解冻
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unfreeze(Account account, BigDecimal amount, String operatorName) {
        amount = amount.abs();
        OperateFreezeVO operateFreezeVO = new OperateFreezeVO(account.getId(), amount);
        baseMapper.operateUnfreeze(operateFreezeVO);

        // 保存操作记录
        String description = operatorName + "操作" + account.getAccountTypeName() + "，解冻" + amount + UNIT;
        this.saveOperateRecord(account, operatorName, OperateEventEnum.UNFREEZE_MONEY.getText(), description);
    }


    /**
     * 解冻扣除
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unfreezeDeduction(Account account, BigDecimal amount, String operatorName) {
        amount = amount.abs();
        OperateFreezeVO operateFreezeVO = new OperateFreezeVO(account.getId(), amount);
        baseMapper.operateUnfreezeDeduction(operateFreezeVO);

        // 保存操作记录
        String description = operatorName + "操作" + account.getAccountTypeName() + "，解冻扣除" + amount + UNIT;
        this.saveOperateRecord(account, operatorName, OperateEventEnum.UNFREEZE_DEDUCTION_MONEY.getText(), description);
    }


    /**
     * 保存操作记录
     */
    private void saveOperateRecord(Account account, String updaterName, String eventName, String description) {
        OperateRecord operateRecord = new OperateRecord();
        operateRecord.setObjId(account.getId());
        operateRecord.setObjType(ObjTypeEnum.ACCOUNT.getIndex());
        operateRecord.setCreaterId(account.getUpdaterId());
        operateRecord.setCreaterName(updaterName);
        operateRecord.setCreateTime(new Date());
        operateRecord.setEventName(eventName);
        operateRecord.setDescription(description);
        operateRecord.setOrgId(account.getOrgId());
        operateRecord.setMerchantId(account.getMerchantId());
        operateRecordService.save(operateRecord);
    }
}
